﻿@page "/pages/authentication/register"
@using CleanArchitecture.Blazor.Application.Common.Interfaces.MultiTenant
@using CleanArchitecture.Blazor.Application.Features.Tenants.DTOs
@using CleanArchitecture.Blazor.Domain.Identity
@using CleanArchitecture.Blazor.Infrastructure.Constants.Localization
@using CleanArchitecture.Blazor.Infrastructure.Constants.Role
@using Microsoft.AspNetCore.WebUtilities
@using System.Text
@using CleanArchitecture.Blazor.Application.Features.Identity.Notifications.UserActivation
@using System.ComponentModel.DataAnnotations
@using System.Globalization

@inject UserManager<ApplicationUser> UserManager
@inject IStringLocalizer<Register> L
@inject ILogger<Register> Logger
@inject IUserStore<ApplicationUser> UserStore
@inject SignInManager<ApplicationUser> SignInManager
@inject IdentityRedirectManager RedirectManager
@inject ITenantService TenantsService
<PageTitle>@Title</PageTitle>

<div class="d-flex flex-column gap-y-3">
    <div class="d-flex flex-column">
        <EditForm Model="Input" asp-route-returnUrl="@ReturnUrl" method="post" OnValidSubmit="RegisterUser" FormName="register">
            <DataAnnotationsValidator />
            <MudText Typo="Typo.h4" GutterBottom="true">@L["Sign Up"]</MudText>
            <MudText>
                @L["Have an account?"] <MudLink Href="@Login.PageUrl">@L["Sign In"]</MudLink>
            </MudText>
            <StatusMessage Error="true" Message="@message"></StatusMessage>
            <div class="mud-input-control mud-input-outlined-with-label my-4">
                <div class="mud-input-control-input-container">
                    <div class="mud-input mud-input-outlined mud-input-outlined-with-label mud-shrink mud-typography-subtitle1">
                        <InputSelect @bind-Value="Input.TenantId" class="mud-input-slot mud-input-root mud-input-root-outlined" style="padding: 18.5px 14px;
                                                                                                                                       width:100%;
                                                                                                                                       appearance: none;
                                                                                                                                       background: url('') no-repeat right 10px center;
                                                                                                                                       background-size: 10px;" aria-required="true">
                            @foreach (var tenant in TenantsService.DataSource)
                            {
                                <option value="@tenant.Id">@tenant.Name</option>
                            }
                        </InputSelect>
                        <div class="mud-input-slot mud-input-root mud-input-root-outlined mud-input-root-adorned-end" style="display:none"></div>
                        <fieldset class="mud-input-outlined-border"><legend>@L["Tenant"]</legend></fieldset>
                    </div>
                    <label class="mud-input-label mud-input-label-animated mud-input-label-outlined mud-input-label-inputcontrol" for="TenantId">@L["Tenant"]</label>
                </div>
                <div class="mud-input-control-helper-container">
                    <div class="mud-input-helper-text mud-input-error">
                        <div class="d-flex mud-input-helper-text mud-input-error">
                            <ValidationMessage For="() => Input.TenantId" class="mud-input-error" />
                        </div>
                    </div>
                </div>
            </div>
            <div class="mud-input-control mud-input-outlined-with-label mud-field my-4">
                <div class="mud-input-control-input-container">
                    <!--!--><!--!-->
                    <div class="mud-input mud-input-outlined mud-input-outlined-with-label mud-shrink mud-typography-subtitle1">
                        <InputText @bind-Value="Input.UserName" class="mud-input-slot mud-input-root mud-input-root-outlined" type="text" autocomplete="username" aria-required="true" placeholder="user name" />
                        <div class="mud-input-slot mud-input-root mud-input-root-outlined" style="display:none"></div>
                        <!--!-->
                        <fieldset class="mud-input-outlined-border"><legend>@L["User Name"]</legend></fieldset>
                    </div>
                    <!--!-->
                    <label class="mud-input-label mud-input-label-animated mud-input-label-outlined mud-input-label-inputcontrol" for="userName">@L["User Name"]</label>
                </div>
                <div class="mud-input-control-helper-container">
                    <div class="mud-input-helper-text mud-input-error">
                        <div class="d-flex mud-input-helper-text mud-input-error">
                            <ValidationMessage For="() => Input.UserName" class="mud-input-error" />
                        </div>
                    </div>
                </div>
            </div>


            <div class="mud-input-control mud-input-outlined-with-label mud-field my-4">
                <div class="mud-input-control-input-container">
                    <!--!--><!--!-->
                    <div class="mud-input mud-input-outlined mud-input-outlined-with-label mud-shrink mud-typography-subtitle1">
                        <InputText @bind-Value="Input.Email" class="mud-input-slot mud-input-root mud-input-root-outlined" type="text" autocomplete="username" aria-required="true" placeholder="user name" />
                        <div class="mud-input-slot mud-input-root mud-input-root-outlined" style="display:none"></div>
                        <!--!-->
                        <fieldset class="mud-input-outlined-border"><legend>@L["E-mail"]</legend></fieldset>
                    </div>
                    <!--!-->
                    <label class="mud-input-label mud-input-label-animated mud-input-label-outlined mud-input-label-inputcontrol" for="userName">@L["E-mail"]</label>
                </div>
                <div class="mud-input-control-helper-container">
                    <div class="mud-input-helper-text mud-input-error">
                        <div class="d-flex mud-input-helper-text mud-input-error">
                            <ValidationMessage For="() => Input.Email" class="mud-input-error" />
                        </div>
                    </div>
                </div>
            </div>

            <div class="mud-input-control mud-input-outlined-with-label mud-field my-4">
                <div class="mud-input-control-input-container">
                    <!--!--><!--!-->
                    <div class="mud-input mud-input-outlined mud-input-outlined-with-label mud-shrink mud-typography-subtitle1">
                        <InputText type="password" @bind-Value="Input.Password" class="mud-input-slot mud-input-root mud-input-root-outlined" autocomplete="current-password" aria-required="true" placeholder="password" />
                        <div class="mud-input-slot mud-input-root mud-input-root-outlined" style="display:none"></div>
                        <!--!-->
                        <fieldset class="mud-input-outlined-border"><legend>@L["Password"]</legend></fieldset>
                    </div>
                    <!--!-->
                    <label class="mud-input-label mud-input-label-animated mud-input-label-outlined mud-input-label-inputcontrol" for="password">@L["Password"]</label>
                </div>
                <div class="mud-input-control-helper-container">
                    <div class="mud-input-helper-text mud-input-error">
                        <div class="d-flex mud-input-helper-text mud-input-error">
                            <ValidationMessage For="() => Input.Password" class="mud-input-error" />
                        </div>
                    </div>
                </div>
            </div>
            <div class="mud-input-control mud-input-outlined-with-label mud-field my-4">
                <div class="mud-input-control-input-container">
                    <!--!--><!--!-->
                    <div class="mud-input mud-input-outlined mud-input-outlined-with-label mud-shrink mud-typography-subtitle1">
                        <InputText type="password" @bind-Value="Input.ConfirmPassword" class="mud-input-slot mud-input-root mud-input-root-outlined" autocomplete="current-password" aria-required="true" placeholder="Confirm password" />
                        <div class="mud-input-slot mud-input-root mud-input-root-outlined" style="display:none"></div>
                        <!--!-->
                        <fieldset class="mud-input-outlined-border"><legend>@L["Confirm Password"]</legend></fieldset>
                    </div>
                    <!--!-->
                    <label class="mud-input-label mud-input-label-animated mud-input-label-outlined mud-input-label-inputcontrol" for="password">@L["Confirm Password"]</label>
                </div>
                <div class="mud-input-control-helper-container">
                    <div class="mud-input-helper-text mud-input-error">
                        <div class="d-flex mud-input-helper-text mud-input-error">
                            <ValidationMessage For="() => Input.ConfirmPassword" class="mud-input-error" />
                        </div>
                    </div>
                </div>
            </div>
            <div class="mud-input-control mud-input-outlined-with-label mud-field my-4">
                <div class="mud-input-control-input-container">
                    <div class="mud-input mud-input-outlined mud-input-outlined-with-label mud-shrink mud-typography-subtitle1">
                        <InputSelect @bind-Value="Input.TimeZoneId" class="mud-input-slot mud-input-root mud-input-root-outlined" style="padding: 18.5px 14px;
                                                                                                                                       width:100%;
                                                                                                                                       appearance: none;
                                                                                                                                       background: url('') no-repeat right 10px center;
                                                                                                                                       background-size: 10px;" aria-required="true">
                            @foreach (var item in TimeZoneInfo.GetSystemTimeZones().ToList())
                            {
                                <option value="@item.Id">@item.DisplayName</option>
                            }
                        </InputSelect>
                        <fieldset class="mud-input-outlined-border"><legend>@L["Time Zone"]</legend></fieldset>
                    </div>
                    <label class="mud-input-label mud-input-label-animated mud-input-label-outlined mud-input-label-inputcontrol" for="TenantId">@L["Time Zone"]</label>
                </div>
                <div class="mud-input-control-helper-container">
                    <div class="mud-input-helper-text mud-input-error">
                        <div class="d-flex mud-input-helper-text mud-input-error">
                            <ValidationMessage For="() => Input.TimeZoneId" class="mud-input-error" />
                        </div>
                    </div>
                </div>
            </div>
            <div class="mud-input-control mud-input-outlined-with-label mud-field my-4">
                <div class="mud-input-control-input-container">
                    <div class="mud-input mud-input-outlined mud-input-outlined-with-label mud-shrink mud-typography-subtitle1">
                        <InputSelect @bind-Value="Input.LanguageCode" class="mud-input-slot mud-input-root mud-input-root-outlined" style="padding: 18.5px 14px;
                                                                                                                                       width:100%;
                                                                                                                                       appearance: none;
                                                                                                                                       background: url('') no-repeat right 10px center;
                                                                                                                                       background-size: 10px;" aria-required="true">
                            @foreach (var item in LocalizationConstants.SupportedLanguages.ToList())
                            {
                                <option value="@item.Code">@item.DisplayName</option>
                            }
                        </InputSelect>
                        <fieldset class="mud-input-outlined-border"><legend>@L["Language"]</legend></fieldset>
                    </div>
                    <label class="mud-input-label mud-input-label-animated mud-input-label-outlined mud-input-label-inputcontrol" for="TenantId">@L["Language"]</label>
                </div>
                <div class="mud-input-control-helper-container">
                    <div class="mud-input-helper-text mud-input-error">
                        <div class="d-flex mud-input-helper-text mud-input-error">
                            <ValidationMessage For="() => Input.LanguageCode" class="mud-input-error" />
                        </div>
                    </div>
                </div>
            </div>
            <div Class="d-flex justify-space-between align-center mb-1">
                <label class="form-label">
                    <InputCheckbox @bind-Value="Input.AgreeToTerms" class="form-check-input" />
                    @L["I agree to the terms and privacy"]
                </label>
                <div class="mud-input-helper-text mud-input-error">
                    <div class="d-flex">
                        <ValidationMessage For="() => Input.AgreeToTerms" class="mud-input-error" />
                    </div>
                </div>
            </div>

            <MudButton Variant="Variant.Filled"
                       Color="Color.Primary"
                       Size="Size.Large"
                       ButtonType="ButtonType.Submit"
                       FullWidth="true">
                <MudText>@L["Register"]</MudText>
            </MudButton>
        </EditForm>
    </div>
</div>

@code {

    public const string PageUrl = "/pages/authentication/register";
    private string Title = "Sign Up";

    [SupplyParameterFromQuery] private string? ReturnUrl { get; set; }
    private string? message;


    [SupplyParameterFromForm] private InputModel Input { get; set; } = new();

    protected override void OnInitialized()
    {
        Title = L["Sign Up"];


    }

    public async Task RegisterUser(EditContext editContext)
    {
        var user = CreateUser();
        user.TenantId = Input.TenantId;
        user.LanguageCode = Input.LanguageCode;
        user.TimeZoneId = Input.TimeZoneId;
        await UserStore.SetUserNameAsync(user, Input.UserName, CancellationToken.None);
        var emailStore = GetEmailStore();
        await emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
        var result = await UserManager.CreateAsync(user, Input.Password);
        if (!result.Succeeded)
        {
            message = string.Join(", ", result.Errors.Select(error => error.Description));
            return;
        }

        result = await UserManager.AddToRoleAsync(user, RoleName.Basic);
        if (!result.Succeeded)
        {
            message = string.Join(", ", result.Errors.Select(error => error.Description));
            return;
        }

        var userId = await UserManager.GetUserIdAsync(user);
        Logger.LogInformation("New user account created. Username: {UserName}, User ID: {UserId}, Tenant ID: {TenantId}, Assigned Role: {RoleName}.",
            Input.UserName,
            userId,
            Input.TenantId,
            RoleName.Basic);
        if (UserManager.Options.SignIn.RequireConfirmedEmail)
        {
            var code = await UserManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            var callbackUrl = Navigation.GetUriWithQueryParameters(
                Navigation.ToAbsoluteUri(ConfirmEmail.PageUrl).AbsoluteUri,
                new Dictionary<string, object?> { ["userId"] = userId, ["code"] = code, ["returnUrl"] = "/" });
            await Mediator.Publish(new UserActivationNotification(callbackUrl, Input.Email, userId, Input.UserName));
            RedirectManager.RedirectTo(RegisterConfirmation.PageUrl, new Dictionary<string, object?> { ["email"] = Input.Email });
        }
        else if (UserManager.Options.SignIn.RequireConfirmedAccount)
        {
            var code = await UserManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            var callbackUrl = Navigation.GetUriWithQueryParameters(
                Navigation.ToAbsoluteUri(ConfirmEmail.PageUrl).AbsoluteUri,
                new Dictionary<string, object?> { ["userId"] = userId, ["code"] = code });
            RedirectManager.RedirectTo(RegisterConfirmation.PageUrl, new Dictionary<string, object?> { ["email"] = Input.Email, ["EmailConfirmationLink"] = callbackUrl });
        }

        await SignInManager.SignInAsync(user, false);
        RedirectManager.RedirectTo(ReturnUrl);
    }

    private ApplicationUser CreateUser()
    {
        try
        {
            return Activator.CreateInstance<ApplicationUser>();
        }
        catch
        {
            throw new InvalidOperationException($"Can't create an instance of '{nameof(ApplicationUser)}'. " +
                                                $"Ensure that '{nameof(ApplicationUser)}' is not an abstract class and has a parameterless constructor.");
        }
    }

    private IUserEmailStore<ApplicationUser> GetEmailStore()
    {
        if (!UserManager.SupportsUserEmail)
        {
            throw new NotSupportedException(L["The default UI requires a user store with email support."]);
        }

        return (IUserEmailStore<ApplicationUser>)UserStore;
    }

    public sealed class InputModel
    {
        [Required]
        [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 2)]
        public string UserName { get; set; } = "";

        [Required]
        [EmailAddress]
        [Display(Name = "Email")]
        public string Email { get; set; } = "";

        [Required]
        [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string Password { get; set; } = "";

        [DataType(DataType.Password)]
        [Display(Name = "Confirm password")]
        [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
        public string? ConfirmPassword { get; set; } = "";

        [Range(typeof(bool), "true", "true", ErrorMessage = "You must agree to the terms.")]
        public bool AgreeToTerms { get; set; } = true;

        [Required]
        [Display(Name = "Tenant Id")]
        public string TenantId { get; set; } = "";
        [Display(Name = "Time Zone")]
        public string? TimeZoneId { get; set; } = TimeZoneInfo.Local.Id;
        [Display(Name = "Language")]
        public string? LanguageCode { get; set; } = CultureInfo.CurrentCulture.Name;
    }

}